﻿using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

namespace Jayden.Dll.BitTorrent
{
	public class BEncoding
	{
		public int Offset;
		public int Length;
		public byte[] Hash;

		public object ReadObject(byte[] data, ref int offset)
		{
			switch (data[offset])
			{
				case (byte)'d':
					return ReadDictionary(data, ref offset);
				case (byte)'l':
					return ReadList(data, ref offset);
				case (byte)'i':
					return ReadInteger(data, ref offset);
			}
			if (char.IsDigit((char)data[offset]))
				return ReadByteArray(data, ref offset);
			throw new Exception("Invalid BEncoded object.");
		}

		public byte[] ReadByteArray(byte[] data, ref int offset)
		{
			StringBuilder builder = new StringBuilder();
			while (char.IsDigit((char)data[offset]))
			{
				builder.Append((char)data[offset]);
				offset++;
			}
			if (data[offset] != ':')
				throw new Exception("Invalid BEncoded string.");
			offset++;
			int byteLength = int.Parse(builder.ToString());
			byte[] array = new byte[byteLength];
			for (int c = 0; c < byteLength; c++)
			{
				array[c] = data[offset];
				offset++;
			}
			return array;
		}
		public string ReadString(byte[] data, ref int offset)
		{
			byte[] bytes = ReadByteArray(data, ref offset);
			return Encoding.UTF8.GetString(bytes);
		}
		public long ReadInteger(byte[] data, ref int offset)
		{
			if (data[offset] != 'i')
				throw new Exception("Invalid BEncoded integer.");
			offset++;
			StringBuilder builder = new StringBuilder();
			while (char.IsDigit((char)data[offset]) || data[offset] == '-')
			{
				builder.Append((char)data[offset]);
				offset++;
			}
			if (data[offset] != 'e')
				throw new Exception("Invalid BEncoded integer.");
			offset++;
			return long.Parse(builder.ToString());
		}
		public List<object> ReadList(byte[] data, ref int offset)
		{
			if (data[offset] != 'l')
				throw new Exception("Invalid BEncoded list.");
			offset++;
			List<object> list = new List<object>();
			while (data[offset] != 'e')
				list.Add(ReadObject(data, ref offset));
			offset++;
			return list;
		}
		public Dictionary<string, object> ReadDictionary(byte[] data, ref int offset)
		{
			if (data[offset] != 'd')
				throw new Exception("Invalid BEncoded dictionary.");
			offset++;
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			while (data[offset] != 'e')
			{
				string key = ReadString(data, ref offset);
				int startOffset = offset;
				if (key == "info")
					Offset = offset;
				object value = ReadObject(data, ref offset);
				if (key == "info")
				{
					Length = (offset - startOffset);
					SHA1CryptoServiceProvider sha1 = new SHA1CryptoServiceProvider();
					Hash = sha1.ComputeHash(data, Offset, Length);
				}
				dictionary.Add(key, value);
			}
			offset++;
			return dictionary;
		}
	}
}
